k8s(kubernetes) 使用leader election实现选举

您所在的位置:网站首页 elect vote区别 k8s(kubernetes) 使用leader election实现选举

k8s(kubernetes) 使用leader election实现选举

#k8s(kubernetes) 使用leader election实现选举| 来源: 网络整理| 查看: 265

一、背景

在kubernetes的世界中,很多组件仅仅需要一个实例在运行,比如controller-manager或第三方的controller,但是为了高可用性,需要组件有多个副本,在发生故障的时候需要自动切换。因此,需要利用leader election的机制多副本部署,单实例运行的模式。应用程序可以使用外部的组件比如ZooKeeper或Etcd等中间件进行leader eleaction, ZooKeeper的实现是采用临时节点的方案,临时节点存活与客户端与ZooKeeper的会话期间,在会话结束后,临时节点会被立刻删除,临时节点被删除后,其他处于被动状态的服务实例会竞争生成临时节点,生成临时节点的客户端(服务实例)就变成Leader,从而保证整个集群中只有一个活跃的实例,在发生故障的时候,也能快速的实现主从之间的迁移。Etcd是一个分布式的kv存储组件,利用Raft协议维护副本的状态服务,Etcd的Revision机制可以实现分布式锁的功能,Etcd的concurrency利用的分布式锁的能力实现了选Leader的功能(本文更多关注的是k8s本身的能力,Etcd的concurrency机制不做详细介绍)。

kubernetes使用的Etcd作为底层的存储组件,因此我们是不是有可能利用kubernetes的API实现选leader的功能呢?其实kubernetes的SIG已经提供了这方面的能力,主要是通过configmap/lease/endpoint的资源实现选Leader的功能。

二、官网代码示例

kubernetes官方提供了一个使用的例子,源码在:github.com/kubernetes/…

选举的过程中,每个实例的状态有可能是:

选择成功->运行业务代码 等待状态,有其他实例成为了leader。当leader放弃锁后,此状态的实例有可能会成为新的leader 释放leader的锁,在运行的业务代码退出

在稳定的环境中,实例一旦成为了leader,通常情况是不会释放锁的,会保持一直运行的状态,这样有利于业务的稳定和Controller快速的对资源的状态变化做成相应的操作。只有在网络不稳定或误操作删除实例的情况下,才会触发leader的重新选举。

kubernetes官方提供的选举例子详解如下:

package main ​ import ( "context" "flag" "os" "os/signal" "syscall" "time" ​ "github.com/google/uuid" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" clientset "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/leaderelection" "k8s.io/client-go/tools/leaderelection/resourcelock" "k8s.io/klog/v2" ) ​ func buildConfig(kubeconfig string) (*rest.Config, error) { if kubeconfig != "" { cfg, err := clientcmd.BuildConfigFromFlags("", kubeconfig) if err != nil { return nil, err } return cfg, nil } ​ cfg, err := rest.InClusterConfig() if err != nil { return nil, err } return cfg, nil } ​ func main() { klog.InitFlags(nil) ​ var kubeconfig string var leaseLockName string var leaseLockNamespace string var id string  // kubeconfig 指定了kubernetes集群的配置文文件路径 flag.StringVar(&kubeconfig, "kubeconfig", "", "absolute path to the kubeconfig file")  // 锁的拥有者的ID,如果没有传参数进来,就随机生成一个 flag.StringVar(&id, "id", uuid.New().String(), "the holder identity name")  // 锁的ID,对应kubernetes中资源的name flag.StringVar(&leaseLockName, "lease-lock-name", "", "the lease lock resource name")  // 锁的命名空间 flag.StringVar(&leaseLockNamespace, "lease-lock-namespace", "", "the lease lock resource namespace")  // 解析命令行参数 flag.Parse() ​ if leaseLockName == "" { klog.Fatal("unable to get lease lock resource name (missing lease-lock-name flag).") } if leaseLockNamespace == "" { klog.Fatal("unable to get lease lock resource namespace (missing lease-lock-namespace flag).") } ​ // leader election uses the Kubernetes API by writing to a // lock object, which can be a LeaseLock object (preferred), // a ConfigMap, or an Endpoints (deprecated) object. // Conflicting writes are detected and each client handles those actions // independently. config, err := buildConfig(kubeconfig) if err != nil { klog.Fatal(err) }  // 获取kubernetes集群的客户端,如果获取不到,就抛异常退出 client := clientset.NewForConfigOrDie(config)  // 模拟Controller的逻辑代码 run := func(ctx context.Context) { // complete your controller loop here klog.Info("Controller loop...")    // 不退出 select {} } ​ // use a Go context so we can tell the leaderelection code when we // want to step down ctx, cancel := context.WithCancel(context.Background()) defer cancel() ​ // listen for interrupts or the Linux SIGTERM signal and cancel // our context, which the leader election code will observe and // step down  // 处理系统的系统,收到SIGTERM信号后,会退出进程 ch := make(chan os.Signal, 1) signal.Notify(ch, os.Interrupt, syscall.SIGTERM) go func() { 0 && le.observedTime.Add(le.config.LeaseDuration).After(now.Time) && !le.IsLeader() { klog.V(4).Infof("lock is held by %v and has not yet expired", oldLeaderElectionRecord.HolderIdentity) return false } ​ // 3. We're going to try to update. The leaderElectionRecord is set to it's default // here. Let's correct it before updating. // 如果是leader,就更新时间RenewTime,保证其他实例(非主)可以观察到:主还活着 if le.IsLeader() { leaderElectionRecord.AcquireTime = oldLeaderElectionRecord.AcquireTime leaderElectionRecord.LeaderTransitions = oldLeaderElectionRecord.LeaderTransitions } else { // 不是leader,那么锁就发生了转移 leaderElectionRecord.LeaderTransitions = oldLeaderElectionRecord.LeaderTransitions + 1 } // 更新锁 // update the lock itself if err = le.config.Lock.Update(ctx, leaderElectionRecord); err != nil { klog.Errorf("Failed to update lock: %v", err) return false } ​ le.setObservedRecord(&leaderElectionRecord) return true } ​


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3